import type { DevtoolsFormatter, DevtoolsFormatterItem, DevtoolsFormatterObjectConfig } from "./DebugFormatters";

const registry = new Map<any, string>();

export function warn(...data: any[]) {
    console.warn(...data);
}

export function assert(x: boolean): asserts x {
    if (!x) throw new Error('Assertion failed');
}

export function register(handle: any, reason: string) {
    registry.set(handle, reason);
}

export function unregister(handle: any) {
    // if (!registry.has(handle)) warn(handle, 'already unregistered');
    registry.delete(handle);
}

export function checkRegister() {
    if (registry.size > 0) {
        for (const [handle, reason] of registry) {
            warn(reason, ':', handle);
        }
        registry.clear();
    }
}

export function makeCustomStringViewClass<T>(
    type: new (...args: any[]) => T, form: (obj: T) => string | DevtoolsFormatterItem[], 
    body: ((obj: T) => any[]) | undefined = (obj) => [obj]): DevtoolsFormatter 
{
    return makeCustomStringView((obj): obj is T => obj instanceof type, form, body);
}

export function makeCustomStringView<T>(
    checker: (obj: any) => obj is T, form: (obj: T) => string | DevtoolsFormatterItem[], 
    body: ((obj: T) => any[] | any) | undefined = (obj) => obj): DevtoolsFormatter
{
    return makeCustomViewer<T>(
        (obj, config) => checker(obj) && (!config || !config['noshow']), 
        (obj) => {
            let f = form(obj);
            return ['span', ...(Array.isArray(f) ? f : [f])];
        },
        body ? (obj) => {
            let b = body(obj);
            if (Array.isArray(b)) {
                if (b.length == 0) return ['span', ''];
                return ['ol', {style: 'list-style: none; padding: 0; margin: 0'}, 
                    ...b.map((x) => ['li', {style: 'line-height: 1.2'},
                        ['span', {style: 'padding: 0 1ch; color: darkblue'}, '*'],
                        ['object', {object: x}]] as const)];
            } else
                return ['object', {object: b[0], config: {noshow: true}}] as const;
        } : undefined);
}

export function makeCustomViewerClass<T>(
    type: new (...args: any[]) => T,
    header: (obj: T, config?: DevtoolsFormatterObjectConfig) => DevtoolsFormatterItem, 
    body?: (obj: T, config?: DevtoolsFormatterObjectConfig) => DevtoolsFormatterItem | null): DevtoolsFormatter 
{
    return makeCustomViewer((obj): obj is T => obj instanceof type, header, body);
}

export function makeCustomViewer<T>(
    checker: (obj: any, config?: DevtoolsFormatterObjectConfig) => boolean, 
    header: (obj: T, config?: DevtoolsFormatterObjectConfig) => DevtoolsFormatterItem, 
    body?: (obj: T, config?: DevtoolsFormatterObjectConfig) => DevtoolsFormatterItem | null): DevtoolsFormatter 
{
    let formatter: DevtoolsFormatter = {
        header: (obj, config) => checker(obj, config) ? header(obj, config) : null,
        hasBody: (obj) => !!body && body(obj, undefined) !== null && checker(obj),
        body: (obj, config) => body!(obj, config)!
    };
    if (!window.devtoolsFormatters) window.devtoolsFormatters = [];
    window.devtoolsFormatters.push(formatter);
    return formatter;
}